from tkinter import *
from tkinter.ttk import *
from typing import Dict
import pandas as pd
import os,random,sys,codecs,datetime
from datetime import date
import random
import configparser
import logging,easyquotation

#记录日志到本地
def logfun(isfile):

    logger = logging.getLogger()
    # 输出到console
    # logger.setLevel(level=logging.DEBUG)
    logger.setLevel(level=logging.INFO) # 某些python库文件中有一些DEBUG级的输出信息，如果这里设置为DEBUG，会导致console和log文件中写入海量信息
    # console_formatter = colorlog.ColoredFormatter(
    #     # fmt='%(log_color)s[%(asctime)s.%(msecs)03d] %(filename)s -> %(funcName)s line:%(lineno)d [%(levelname)s] : %(message)s',
    #     fmt=' %(levelname)s: %(message)s'
    #     # datefmt='%Y-%m-%d  %H:%M:%S'
    # )
    console = logging.StreamHandler()  # 输出到console的handler
    console.setLevel(logging.INFO)
    # console.setFormatter(console_formatter)
    logger.addHandler(console)
    # 输出到文件
    if isfile:
        # 设置文件名
        now = datetime.datetime.now()
        filename = f"log_{now.strftime('%Y-%m-%d_%H-%M-%S')}.txt"
        log_path=os.path.join(os.getcwd(),'log')
        if not os.path.exists(log_path):
            os.mkdir(log_path)
        logfile = log_path + '/'+filename
        # 设置文件日志格式
        filer = logging.FileHandler(logfile,mode='w') # 输出到log文件的handler
        # filer.setLevel(level=logging.DEBUG)
        file_formatter = logging.Formatter(
            fmt='%(asctime)s - %(levelname)s: %(message)s'
        )
        # formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
        filer.setFormatter(file_formatter)
        logger.addHandler(filer)

    return logger

def ReadFile(filePath):
    with codecs.open(filePath, "r") as f:
        return f.read()

def WriteFile(filePath, u, encoding="utf-8"):
    # with codecs.open(filePath,"w",encoding) as f:
    with codecs.open(filePath, "wb") as f:
        f.write(u.encode(encoding, errors="ignore"))

def CSV_2_UTF8(src, dst):
    content = ReadFile(src)
    WriteFile(dst, content, encoding="utf-8")

def  每手取整(代码,数量,仓位):
    if 代码[0:2] in ["11","12"] :
        取整数量=int(round(数量*仓位,-1))  #可转债
        if 取整数量==0:
            取整数量=10
    elif 代码[0:2]=="68":
        取整数量=int(round(数量*仓位,0))   #科创板
        if 取整数量==0:
            取整数量=200
    else :
        取整数量=int(round(数量*仓位,-2))
        if 取整数量==0:
            取整数量=100
    return 取整数量

#获取本地路径下的基础数据
def joinPath(fileName,subCatalog):
    pyPath=os.path.dirname(os.path.realpath(sys.argv[0]))
    if subCatalog=="":
        filePath=os.path.join(pyPath,fileName)
    else:
        pyPath=os.path.join(pyPath,subCatalog)
        if os.path.exists(pyPath):
            pass
        else:
            os.mkdir(pyPath)
        filePath=os.path.join(pyPath,fileName)
    return filePath

#  指定目录下,将数据带时间后缀保存为csv格式
def saveCsvByTime(fileNanme,saveData,TimeFlag,encondingType,subCatalog):
    # OQPath:导出目录,fileNanme：文件名前缀,saveData：保存的数据，subCatalog是否需要子目录
    now = datetime.datetime.now()
    if TimeFlag==True:
        filename = f"{fileNanme}_{now.strftime('%Y-%m-%d_%H-%M-%S')}.csv"
    else:
        filename = f"{fileNanme}.csv"
    if subCatalog=="":
        full_path = joinPath(filename,"")
    else:
        full_path = joinPath(filename,subCatalog)
    if  encondingType=="ANSI":
        saveData.to_csv(full_path,index=False,encoding="ANSI")
    else:
        saveData.to_csv(full_path,index=False)

# 获取腾讯行情的指定价格类型
def tencentQuo(OQ代码,价格类型):
    # 获取委托价格
    if ".SH" in OQ代码:
        腾讯代码="sh"+OQ代码[0:6]
    elif ".SZ" in OQ代码:
        腾讯代码="sz"+OQ代码[0:6]
    try:
        价格=quotation.real(腾讯代码)[腾讯代码[2:8]][价格类型]
    except:
        价格=0.01
    return 价格


#  读取配置文件信息
pyPath=os.path.dirname(os.path.realpath(sys.argv[0]))
config = configparser.ConfigParser()
配置文件目录=os.path.join(pyPath,"testConfig")
config.read(os.path.join(配置文件目录,'testConfig.ini'),encoding='utf-8')   # 读取config.ini
OQAccount=config.get("login", "OQAccount")
OQCode=config.get("login", "OQCode")
startTime=config.get("login", "startTime")
OQDir=config.get("login", "OQDir")
maxEable=float(config.get("securityAllocation", "maxEable"))  #每笔任务按照20%可用
sloBase=int(config.get("securityAllocation", "sloBase"))   #每100只作为一个基数，融券卖出3笔，买券还券3笔
sloSellBase=int(config.get("securityAllocation", "sloSellBase"))
sloBuyBase=int(config.get("securityAllocation", "sloBuyBase"))
marginRate=float(config.get("securityAllocation", "marginRate"))
finBuyRate=float(config.get("securityAllocation", "finBuyRate"))
buyRate=float(config.get("securityAllocation", "buyRate"))
miniNum=int(config.get("securityAllocation", "miniNum"))

algoMoveUplimit=float(config.get("securityAllocation", "algoMoveUplimit"))  #调仓算法调仓比例上限
algoMoveDownlimit=float(config.get("securityAllocation", "algoMoveDownlimit"))  #调仓算法调仓比例下限
gridOrderNum=float(config.get("securityAllocation", "gridOrderNum"))    #网格交易每次交易底仓数
gridRateUpLimit=float(config.get("securityAllocation", "gridRateUpLimit"))    #网格交易每格幅度上限
gridRateDownLimit=float(config.get("securityAllocation", "gridRateDownLimit"))    #网格交易每格幅度下限
if config.get("outPut", "isLog")=="False":
    isLog=False
else:
    isLog=True  #打印日志推送开关
# 是否保存本地日志
log=logfun(isfile=isLog)


#随机算法结束时间
当前时间=str(datetime.datetime.now().hour)+str(datetime.datetime.now().minute)
随机算法结束时间=[]
if 当前时间<"1500":
    算法结束时间=["100000","103000","110000","113000","130000","133000","140000","143000","145600"]
    for i  in 算法结束时间:
        if i[0:4]>当前时间:
            随机算法结束时间.append(i)
else :
    随机算法结束时间=[(datetime.datetime.now()+datetime.timedelta(minutes=20)).strftime("%H%M%S"),(datetime.datetime.now()+datetime.timedelta(minutes=40)).strftime("%H%M%S"),
          (datetime.datetime.now()+datetime.timedelta(minutes=60)).strftime("%H%M%S"),(datetime.datetime.now()+datetime.timedelta(minutes=80)).strftime("%H%M%S"),
          (datetime.datetime.now()+datetime.timedelta(minutes=100)).strftime("%H%M%S"),(datetime.datetime.now()+datetime.timedelta(minutes=120)).strftime("%H%M%S")]

#加载任务配置列表
log.info(f"当前执行任务的目录为：{pyPath}")
任务配置列表=pd.read_excel(os.path.join(pyPath,"testConfig.xls"))
任务配置列表=任务配置列表[任务配置列表["启用"]=="Y"]
任务资金账号列表=list(set(任务配置列表["账号"].tolist()))  #进行账号去重


class WinGUI(Tk):
    def __init__(self):
        super().__init__()
        self.__win()
        self.tk_label_frame_llqdnzgq = self.__tk_label_frame_llqdnzgq(self)
        self.tk_label_llqdtrcb = self.__tk_label_llqdtrcb( self.tk_label_frame_llqdnzgq) 
        self.tk_input_llqdur9g = self.__tk_input_llqdur9g( self.tk_label_frame_llqdnzgq) 
        self.tk_input_llqdvg2q = self.__tk_input_llqdvg2q( self.tk_label_frame_llqdnzgq) 
        self.tk_label_llqdvkup = self.__tk_label_llqdvkup( self.tk_label_frame_llqdnzgq) 
        self.tk_label_llqdw8s1 = self.__tk_label_llqdw8s1( self.tk_label_frame_llqdnzgq) 
        self.tk_input_llqdwmrb = self.__tk_input_llqdwmrb( self.tk_label_frame_llqdnzgq) 
        self.tk_label_llqe1fr7 = self.__tk_label_llqe1fr7( self.tk_label_frame_llqdnzgq) 
        self.tk_input_llqe1x3l = self.__tk_input_llqe1x3l( self.tk_label_frame_llqdnzgq) 
        self.tk_button_llqe3csy = self.__tk_button_llqe3csy( self.tk_label_frame_llqdnzgq) 
        self.tk_button_llqe40zz = self.__tk_button_llqe40zz( self.tk_label_frame_llqdnzgq) 
        self.tk_label_frame_llqe93hb = self.__tk_label_frame_llqe93hb(self)
        self.tk_label_llqecmk0 = self.__tk_label_llqecmk0( self.tk_label_frame_llqe93hb) 
        self.tk_input_llqedgeo = self.__tk_input_llqedgeo( self.tk_label_frame_llqe93hb) 
        self.tk_label_llqei5ms = self.__tk_label_llqei5ms( self.tk_label_frame_llqe93hb) 
        self.tk_input_llqeklyu = self.__tk_input_llqeklyu( self.tk_label_frame_llqe93hb) 
        self.tk_label_llqekpb6 = self.__tk_label_llqekpb6( self.tk_label_frame_llqe93hb) 
        self.tk_input_llqela6a = self.__tk_input_llqela6a( self.tk_label_frame_llqe93hb) 
        self.tk_label_llqelfrx = self.__tk_label_llqelfrx( self.tk_label_frame_llqe93hb) 
        self.tk_input_llqennlf = self.__tk_input_llqennlf( self.tk_label_frame_llqe93hb) 
        self.tk_label_llqeodis = self.__tk_label_llqeodis( self.tk_label_frame_llqe93hb) 
        self.tk_input_llqeopyc = self.__tk_input_llqeopyc( self.tk_label_frame_llqe93hb) 
        self.tk_label_llqeovio = self.__tk_label_llqeovio( self.tk_label_frame_llqe93hb) 
        self.tk_input_llqeozpe = self.__tk_input_llqeozpe( self.tk_label_frame_llqe93hb) 
        self.tk_label_llqet6gs = self.__tk_label_llqet6gs( self.tk_label_frame_llqe93hb) 
        self.tk_input_llqetaeo = self.__tk_input_llqetaeo( self.tk_label_frame_llqe93hb) 
        self.tk_label_llqeuzws = self.__tk_label_llqeuzws( self.tk_label_frame_llqe93hb) 
        self.tk_input_llqevd7u = self.__tk_input_llqevd7u( self.tk_label_frame_llqe93hb) 
        self.tk_label_llqevnws = self.__tk_label_llqevnws( self.tk_label_frame_llqe93hb) 
        self.tk_input_llqewbza = self.__tk_input_llqewbza( self.tk_label_frame_llqe93hb) 
        self.tk_label_llqewgmk = self.__tk_label_llqewgmk( self.tk_label_frame_llqe93hb) 
        self.tk_input_llqewnka = self.__tk_input_llqewnka( self.tk_label_frame_llqe93hb) 
        self.tk_label_llqf443v = self.__tk_label_llqf443v( self.tk_label_frame_llqe93hb) 
        self.tk_input_llqf4zfz = self.__tk_input_llqf4zfz( self.tk_label_frame_llqe93hb) 
        self.tk_label_llqf53pf = self.__tk_label_llqf53pf( self.tk_label_frame_llqe93hb) 
        self.tk_input_llqf5fbd = self.__tk_input_llqf5fbd( self.tk_label_frame_llqe93hb) 
        self.tk_button_llqf6b4o = self.__tk_button_llqf6b4o( self.tk_label_frame_llqe93hb) 
        self.tk_label_frame_llqf81ie = self.__tk_label_frame_llqf81ie(self)
        self.tk_table_llqf9i51 = self.__tk_table_llqf9i51( self.tk_label_frame_llqf81ie) 
        self.tk_button_llqf8fvd = self.__tk_button_llqf8fvd(self)


    def __win(self):
        self.title("Tkinter布局助手")
        # 设置窗口大小、居中
        width = 594
        height = 609
        screenwidth = self.winfo_screenwidth()
        screenheight = self.winfo_screenheight()
        geometry = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
        self.geometry(geometry)
        self.resizable(width=False, height=False)
        # 自动隐藏滚动条
    def scrollbar_autohide(self,bar,widget):
        self.__scrollbar_hide(bar,widget)
        widget.bind("<Enter>", lambda e: self.__scrollbar_show(bar,widget))
        bar.bind("<Enter>", lambda e: self.__scrollbar_show(bar,widget))
        widget.bind("<Leave>", lambda e: self.__scrollbar_hide(bar,widget))
        bar.bind("<Leave>", lambda e: self.__scrollbar_hide(bar,widget))
    
    def __scrollbar_show(self,bar,widget):
        bar.lift(widget)
    def __scrollbar_hide(self,bar,widget):
        bar.lower(widget)
    
    def vbar(self,ele, x, y, w, h, parent):
        sw = 15 # Scrollbar 宽度
        x = x + w - sw
        vbar = Scrollbar(parent)
        ele.configure(yscrollcommand=vbar.set)
        vbar.config(command=ele.yview)
        vbar.place(x=x, y=y, width=sw, height=h)
        self.scrollbar_autohide(vbar,ele)
    def __tk_label_frame_llqdnzgq(self,parent):
        frame = LabelFrame(parent,text="OQ登录")
        frame.place(x=14, y=8, width=548, height=117)
        return frame
    def __tk_label_llqdtrcb(self,parent):
        label = Label(parent,text="OQ账号",anchor="center", )
        label.place(x=0, y=0, width=55, height=22)
        return label
    def __tk_input_llqdur9g(self,parent):
        ipt = Entry(parent, )
        ipt.insert(0,OQAccount)
        ipt.place(x=62, y=0, width=150, height=22)
        return ipt
    def __tk_input_llqdvg2q(self,parent):
        ipt = Entry(parent,show="*")
        ipt.insert(0,OQCode)
        ipt.place(x=328, y=0, width=216, height=22)
        return ipt
    def __tk_label_llqdvkup(self,parent):
        label = Label(parent,text="OQ密码",anchor="center", )
        label.place(x=270, y=0, width=55, height=22)
        return label
    def __tk_label_llqdw8s1(self,parent):
        label = Label(parent,text="exe目录",anchor="center", )
        label.place(x=270, y=34, width=55, height=22)
        return label
    def __tk_input_llqdwmrb(self,parent):
        ipt = Entry(parent )
        ipt.insert(0,OQDir)
        ipt.place(x=328, y=36, width=217, height=22)
        return ipt
    def __tk_label_llqe1fr7(self,parent):
        label = Label(parent,text="登录时间",anchor="center", )
        label.place(x=0, y=36, width=55, height=22)
        return label
    def __tk_input_llqe1x3l(self,parent):
        ipt = Entry(parent )
        ipt.insert(0,startTime)
        ipt.place(x=62, y=36, width=150, height=22)
        return ipt
    def __tk_button_llqe3csy(self,parent):
        btn = Button(parent, text="保存设置", takefocus=False,)
        btn.place(x=132, y=66, width=91, height=27)
        return btn
    def __tk_button_llqe40zz(self,parent):
        btn = Button(parent, text="手动登录", takefocus=False,)
        btn.place(x=280, y=66, width=91, height=27)
        return btn
    def __tk_label_frame_llqe93hb(self,parent):
        frame = LabelFrame(parent,text="模板参数",)
        frame.place(x=17, y=140, width=548, height=163)
        return frame
    def __tk_label_llqecmk0(self,parent):
        label = Label(parent,text="底仓使用率",anchor="center", )
        label.place(x=0, y=0, width=70, height=22)
        return label
    def __tk_input_llqedgeo(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=76, y=0, width=60, height=22)
        return ipt
    def __tk_label_llqei5ms(self,parent):
        label = Label(parent,text="融券基数",anchor="center", )
        label.place(x=198, y=0, width=70, height=22)
        return label
    def __tk_input_llqeklyu(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=272, y=0, width=60, height=22)
        return ipt
    def __tk_label_llqekpb6(self,parent):
        label = Label(parent,text="融卖基数",anchor="center", )
        label.place(x=376, y=0, width=70, height=22)
        return label
    def __tk_input_llqela6a(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=449, y=0, width=61, height=22)
        return ipt
    def __tk_label_llqelfrx(self,parent):
        label = Label(parent,text="还券基数",anchor="center", )
        label.place(x=0, y=30, width=70, height=22)
        return label
    def __tk_input_llqennlf(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=76, y=30, width=60, height=22)
        return ipt
    def __tk_label_llqeodis(self,parent):
        label = Label(parent,text="两融比例",anchor="center", )
        label.place(x=200, y=30, width=70, height=22)
        return label
    def __tk_input_llqeopyc(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=272, y=30, width=60, height=22)
        return ipt
    def __tk_label_llqeovio(self,parent):
        label = Label(parent,text="普买比例",anchor="center", )
        label.place(x=376, y=30, width=70, height=22)
        return label
    def __tk_input_llqeozpe(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=450, y=30, width=60, height=22)
        return ipt
    def __tk_label_llqet6gs(self,parent):
        label = Label(parent,text="融买比例",anchor="center", )
        label.place(x=0, y=60, width=70, height=22)
        return label
    def __tk_input_llqetaeo(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=76, y=60, width=60, height=22)
        return ipt
    def __tk_label_llqeuzws(self,parent):
        label = Label(parent,text="最低记录数",anchor="center", )
        label.place(x=200, y=60, width=70, height=22)
        return label
    def __tk_input_llqevd7u(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=272, y=60, width=60, height=22)
        return ipt
    def __tk_label_llqevnws(self,parent):
        label = Label(parent,text="调仓上浮%",anchor="center", )
        label.place(x=0, y=88, width=70, height=22)
        return label
    def __tk_input_llqewbza(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=76, y=90, width=60, height=22)
        return ipt
    def __tk_label_llqewgmk(self,parent):
        label = Label(parent,text="调仓下浮%",anchor="center", )
        label.place(x=200, y=90, width=70, height=22)
        return label
    def __tk_input_llqewnka(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=272, y=88, width=60, height=22)
        return ipt
    def __tk_label_llqf443v(self,parent):
        label = Label(parent,text="向上网格%",anchor="center", )
        label.place(x=0, y=118, width=70, height=22)
        return label
    def __tk_input_llqf4zfz(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=76, y=118, width=60, height=22)
        return ipt
    def __tk_label_llqf53pf(self,parent):
        label = Label(parent,text="向下网格%",anchor="center", )
        label.place(x=200, y=118, width=70, height=22)
        return label
    def __tk_input_llqf5fbd(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=272, y=118, width=60, height=22)
        return ipt
    def __tk_button_llqf6b4o(self,parent):
        btn = Button(parent, text="保存设置", takefocus=False,)
        btn.place(x=460, y=110, width=77, height=27)
        return btn
    def __tk_label_frame_llqf81ie(self,parent):
        frame = LabelFrame(parent,text="运行结果",)
        frame.place(x=20, y=318, width=547, height=262)
        return frame
    def __tk_table_llqf9i51(self,parent):
        # 表头字段 表头宽度
        columns = {"菜单":105,"子策略":158,"资金账号":105,"运行结果":132}
        tk_table = Treeview(parent, show="headings", columns=list(columns),)
        for text, width in columns.items():  # 批量设置列属性
            tk_table.heading(text, text=text, anchor='center')
            tk_table.column(text, anchor='center', width=width, stretch=False)  # stretch 不自动拉伸
        
        tk_table.place(x=6, y=4, width=530, height=191)
        self.vbar(tk_table, 6, 4, 530, 191,parent)
        return tk_table
    def __tk_button_llqf8fvd(self,parent):
        btn = Button(parent, text="生成模板", takefocus=False,)
        btn.place(x=490, y=540, width=71, height=27)
        return btn
class Win(WinGUI):
    def __init__(self):
        super().__init__()
        self.__event_bind()
        self.WinGUI=WinGUI
    def 保存登录设置(self,evt):
        print("<Button>事件未处理:",evt)
    def 手动登录OQ(self,evt):
        print("<Button>事件未处理:",evt)
    def 保存模板设置(self,evt):
        print("<Button>事件未处理:",evt)
    def 生成模板(self,evt):
        print("<Button>事件未处理:",evt)
    def __event_bind(self):
        self.tk_button_llqe3csy.bind('<Button>',self.保存登录设置)
        self.tk_button_llqe40zz.bind('<Button>',self.手动登录OQ)
        self.tk_button_llqf6b4o.bind('<Button>',self.保存模板设置)
        self.tk_button_llqf8fvd.bind('<Button>',self.生成模板)
        pass
if __name__ == "__main__":
    win = Win()
    win.mainloop()
